//
// Created by Felix on 24-4-25.
//

#include "UserService.hpp"

oatpp::Object<UserResponseDto> UserService::userResponseDtoFromModel(
        const oatpp::Object<UserModel> &model
) {
    auto dto = UserResponseDto::createShared();
    dto->userId = model->id;
    dto->username = model->username;
    dto->avatar = model->avatar;
    dto->role = model->role;
    dto->status = model->status;
    dto->createTime = model->createTime;
    dto->updateTime = model->updateTime;
    dto->deleteTime = model->deleteTime;
    return dto;
}

oatpp::Object<UserModel> UserService::userModelFromCreateDto(
        const oatpp::Object<UserCreateDto> &dto
) {
    auto model = UserModel::createShared();
    model->id = nullptr;
    model->username = dto->username;
    model->password = sha256(dto->password + "gaze");
    model->role = dto->role;
    model->status = 1;
    model->createTime = getCurrentTime();
    model->updateTime = model->createTime;
#ifdef __APPLE__
    model->deleteTime = 0LL;
#else
    model->deleteTime = 0L;
#endif
    return model;
}

oatpp::Object<UserModel> UserService::userModelFromUpdateDetailsDto(
        const oatpp::Object<UserUpdateDetailsDto> &dto
) {
    auto model = UserModel::createShared();
    model->id = dto->userId;
    model->username = dto->username;
    model->role = dto->role;
    model->status = dto->status;
    model->updateTime = getCurrentTime();
    return model;
}

oatpp::Object<UserModel> UserService::userModelFromUpdateStatusDto(
        const oatpp::Object<UserUpdateStatusDto> &dto
) {
    auto model = UserModel::createShared();
    model->id = dto->userId;
    model->status = dto->status;
    model->updateTime = getCurrentTime();
    return model;
}

oatpp::Object<UserModel> UserService::userModelFromUpdatePasswordDto(
        const oatpp::Object<UserUpdatePasswordDto> &dto
) {
    auto model = UserModel::createShared();
    model->id = dto->userId;
    model->password = dto->password;
    model->updateTime = getCurrentTime();
    return model;
}

oatpp::Object<UserResponseDto> UserService::createUser(
        const oatpp::Object<UserCreateDto> &dto
) {
    auto dbResult = iUserDatabase->createUser(userModelFromCreateDto(dto));
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error")

    return userResponseDtoFromModel(result[0]);
}

oatpp::Object<UserResponseDto> UserService::updateUserDetails(
        const oatpp::Object<UserUpdateDetailsDto> &dto
) {
    auto dbResult = iUserDatabase->updateUserDetails(userModelFromUpdateDetailsDto(dto));
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error")

    return userResponseDtoFromModel(result[0]);
}

oatpp::Object<UserResponseDto> UserService::updateUserStatus(
        const oatpp::Object<UserUpdateStatusDto> &dto
) {
    auto dbResult = iUserDatabase->updateUserStatus(userModelFromUpdateStatusDto(dto));
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error")

    return userResponseDtoFromModel(result[0]);
}

oatpp::Object<UserResponseDto> UserService::updateUserPassword(
        const oatpp::Object<UserUpdatePasswordDto> &dto
) {
    auto dbResult = iUserDatabase->updateUserPassword(userModelFromUpdatePasswordDto(dto));
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error")

    return userResponseDtoFromModel(result[0]);
}

oatpp::Object<UserResponseDto> UserService::getUserById(
        const oatpp::Int32 &userId
) {
    auto dbResult = iUserDatabase->getUserById(userId);
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    if (!result->empty()) {
        return userResponseDtoFromModel(result[0]);
    }

    return nullptr;
}

oatpp::Object<UserResponseDto> UserService::getUserByUsername(
        const oatpp::String &username
) {
    auto dbResult = iUserDatabase->getUserByUsername(username);
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    if (!result->empty()) {
        return userResponseDtoFromModel(result[0]);
    }

    return nullptr;
}

oatpp::Object<UserResponseDto> UserService::getUserByUsernameFilterOutUserId(
        const oatpp::String &username,
        const oatpp::Int32 &userId
) {
    auto dbResult = iUserDatabase->getUserByUsernameFilterOutUserId(
            username, userId
    );
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())

    auto result = dbResult->fetch<oatpp::Vector<oatpp::Object<UserModel>>>();
    if (!result->empty()) {
        return userResponseDtoFromModel(result[0]);
    }

    return nullptr;
}

oatpp::Object<PageResponseDto<oatpp::Object<UserResponseDto>>> UserService::getAllUsers(
        const oatpp::Int32 &page, const oatpp::Int32 &pageSize,
        const oatpp::Object<UserServiceGetAllUsersQueryDto> &queryDto
) {
    oatpp::Int32 p = std::max(page, oatpp::Int32(1));
    oatpp::Int32 offset = (p - 1) * pageSize;
    oatpp::Int32 limit = std::min(std::max(pageSize, oatpp::Int32(1)), oatpp::Int32(50));

    std::unordered_map<oatpp::String, oatpp::Void> params = {
            {"offset", offset},
            {"limit",  limit},
    };
    std::vector<std::string> queryLines = {
            "SELECT COUNT(id)",
            "FROM user",
            "WHERE delete_time = 0",
    };
    if (!queryDto->keyWords->empty()) {
        params["keyWords"] = oatpp::String("%" + queryDto->keyWords + "%");
        queryLines.emplace_back("AND username LIKE :keyWords");
    }
    if (!queryDto->username->empty()) {
        params["username"] = queryDto->username;
        queryLines.emplace_back("AND username = :username");
    }
    if (!queryDto->role->empty()) {
        params["role"] = queryDto->role;
        queryLines.emplace_back("AND role = :role");
    }
    if (queryDto->status > 0) {
        params["status"] = queryDto->status;
        queryLines.emplace_back("AND status = :status");
    }
    oatpp::String query = joinWithSpaces(queryLines);
    auto dbCountResult = iUserDatabase->executeQuery(query, params);
    OATPP_ASSERT_HTTP(dbCountResult->isSuccess(), Status::CODE_500, dbCountResult->getErrorMessage())
    auto total = dbCountResult->fetch<oatpp::Vector<oatpp::Vector<oatpp::Int32>>>()[0][0];

    queryLines[0] = "SELECT id as user_id, username, role, status, create_time, update_time, delete_time";
    queryLines.emplace_back("ORDER BY id DESC");
    queryLines.emplace_back("LIMIT :limit OFFSET :offset");
    query = joinWithSpaces(queryLines);
    auto dbResult = iUserDatabase->executeQuery(query, params);
    OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, dbResult->getErrorMessage())
    auto items = dbResult->fetch<oatpp::Vector<oatpp::Object<UserResponseDto>>>();

    auto dto = PageResponseDto<oatpp::Object<UserResponseDto> >::createShared();
    dto->current = p;
    dto->pageSize = limit;
    dto->total = total;
    dto->items = items;

    return dto;
}
